/* 
 * Copyright (C) 2004-2005 Jonathan Bindel
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

using System;
using System.Collections;
using System.Collections.Generic;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Objects;

namespace DCSharp.Backend.Managers
{
	/// <summary>
	/// The online users on a hub.
	/// </summary>
	public class UserManager : IEnumerable<Identity>
	{
		public event EventHandler<IdentityEventArgs> UserOnline;
		public event EventHandler<IdentityEventArgs> UserOffline;
		public event EventHandler<IdentityEventArgs> UserChanged;

		private Dictionary<User, Identity> users;
		private HubConnection hub;

		public UserManager(HubConnection hub)
		{
			if (hub == null)
			{
				throw new ArgumentNullException("hub");
			}
			this.hub = hub;

			users = new Dictionary<User, Identity>();
		}

		#region Properties

		public int Count
		{
			get { return users.Count; }
		}

		public Identity this[User user]
		{
			get
			{
				lock (users)
				{
					return users.ContainsKey(user) ? users[user] : null;
				}
			}
		}

		public object SyncRoot
		{
			get { return users; }
		}

		#endregion

		#region Methods

		internal void Add(Identity identity)
		{
			if (identity == null)
			{
				throw new ArgumentNullException("identity");
			}
			lock (users)
			{
				if (users.ContainsKey(identity.User))
				{
					return;
				}
				users.Add(identity.User, identity);
			}
			identity.User.AddHub(hub);
			OnUserOnline(identity);
		}

		internal void Remove(Identity identity)
		{
			if (identity == null)
			{
				throw new ArgumentNullException("identity");
			}
			lock (users)
			{
				if (!users.ContainsKey(identity.User))
				{
					return;
				}
				users.Remove(identity.User);
			}
			identity.User.RemoveHub(hub);
			OnUserOffline(identity);
		}

		internal void Clear()
		{
			Identity[] identities;
			lock (users)
			{
				identities = new Identity[users.Values.Count];
				users.Values.CopyTo(identities, 0);
			}
			foreach (Identity identity in identities)
			{
				Remove(identity);
			}
		}

		public bool Contains(Identity user)
		{
			return users.ContainsValue(user);
		}

		public Identity Find(Predicate<Identity> match)
		{
			if (match == null)
			{
				throw new ArgumentNullException("match");
			}
			lock (users)
			{
				foreach (Identity user in users.Values)
				{
					if (match(user))
					{
						return user;
					}
				}
			}
			return null;
		}

		public void ForEach(Action<Identity> action)
		{
			if (action == null)
			{
				throw new ArgumentNullException("action");
			}
			lock (users)
			{
				foreach (Identity user in users.Values)
				{
					action(user);
				}
			}
		}

		public IEnumerator<Identity> GetEnumerator()
		{
			return users.Values.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		protected virtual void OnUserOnline(Identity user)
		{
			if (UserOnline != null)
			{
				UserOnline(this, new IdentityEventArgs(user));
			}
		}

		protected virtual void OnUserOffline(Identity user)
		{
			if (UserOffline != null)
			{
				UserOffline(this, new IdentityEventArgs(user));
			}
		}

		protected void OnUserChanged(Identity user)
		{
			if (UserChanged != null)
			{
				UserChanged(this, new IdentityEventArgs(user));
			}
		}

		internal void EmitUserChanged(Identity user)
		{
			OnUserChanged(user);
		}

		#endregion
	}
}
